package com.wanwu.channel.notify.message;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.openservices.ons.api.Message;
import com.aliyun.openservices.ons.api.Producer;
import com.aliyun.openservices.ons.api.SendResult;
import com.wanwu.channel.common.enums.ExceptionEnum;
import com.wanwu.channel.common.enums.TradeStatusEnum;
import com.wanwu.channel.common.exception.SystemException;
import com.wanwu.channel.common.util.HttpUtil;
import com.wanwu.channel.common.util.LogUtil;
import com.wanwu.channel.common.vo.MessageVo;
import com.wanwu.channel.notify.config.NotifyUrlConfig;
import com.wanwu.channel.notify.config.TlConfig;
import jodd.http.HttpResponse;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;

/**
 * @ClassName SendMessageComponent
 * @Description 回调消息发送组件
 * @Author husheng
 * @Date 2019-12-26 16:35
 */
@Component
public class MessageNotifyComponent {

    private static final Logger logger = LoggerFactory.getLogger(MessageNotifyComponent.class);

    @Resource(name = "payNotifyProducer")
    private Producer payNotifyProducer;

    @Value("${mq.pay.notify.topic}")
    private String payNotifyTopic;

    @Resource(name = "refundNotifyProducer")
    private Producer refundNotifyProducer;

    @Value("${mq.refund.notify.topic}")
    private String refundNotifyTopic;

    @Resource(name = "declareNotifyProducer")
    private Producer declareNotifyProducer;

    @Value("${mq.declare.notify.topic}")
    private String declareNotifyTopic;

    @Value("${mq.ding.appKey}")
    private String appKey;

    @Resource
    private NotifyUrlConfig notifyUrlConfig;

    @Resource
    private TlConfig tlConfig;

    /**
     * 支付结果回调走mq，降级方案走http
     * @param reqDto
     */
    public void sendPayNotify(Object reqDto){
        MessageVo messageVo = new MessageVo();
        messageVo.setTopic(payNotifyTopic);
        messageVo.setBody(reqDto);
        messageVo.setDelayTime(0);
        boolean isHttp = sendMessage(messageVo, payNotifyProducer);
        if(isHttp){
            //走http回调
            LogUtil.info(logger, "支付结果走mq回调失败，降级方案走http，请求报文：{0}", JSON.toJSONString(reqDto));
            notifyToPayServer(notifyUrlConfig.getBizPayNotifyUrl(), JSON.toJSONString(reqDto));
        }
    }

    /**
     * 退款结果回调走mq，降级方案走http
     * @param reqDto
     */
    public void sendRefundNotify(Object reqDto){
        MessageVo messageVo = new MessageVo();
        messageVo.setTopic(refundNotifyTopic);
        messageVo.setBody(reqDto);
        messageVo.setDelayTime(0);
        boolean isHttp = sendMessage(messageVo, refundNotifyProducer);
        if(isHttp){
            //走http回调
            LogUtil.info(logger, "退款结果走mq回调失败，降级方案走http，请求报文：{0}", JSON.toJSONString(reqDto));
            refundNotifyToPayServer(notifyUrlConfig.getBizRefundNotifyUrl(), JSON.toJSONString(reqDto));
        }
    }

    /**
     * 报关结果回调走mq，降级方案走http
     * @param reqDto
     */
    public void sendDeclareNotify(Object reqDto){
        MessageVo messageVo = new MessageVo();
        messageVo.setTopic(declareNotifyTopic);
        messageVo.setBody(reqDto);
        messageVo.setDelayTime(0);
        boolean isHttp = sendMessage(messageVo, declareNotifyProducer);
        if(isHttp){
            //走http回调
            LogUtil.info(logger, "报关结果走mq回调失败，降级方案走http，请求报文：{0}", JSON.toJSONString(reqDto));
            declareNotifyToPayServer(tlConfig.getPayDeclareNotifyUrl(), JSON.toJSONString(reqDto));
        }
    }

    /**
     * 发送消息，发送失败重发一次，再次失败则走http回调
     * @param messageVo
     * @param producer
     * @return true-走http回调，false-不走http回调
     */
    public boolean sendMessage(MessageVo messageVo, Producer producer) {
        Message message = toMessage(messageVo);
        try {
            LogUtil.info(logger, "收到推送MQ队列消息 {0}", message);
            SendResult result = producer.send(message);
            LogUtil.info(logger, "MQ消息发送完成 {0}", result);
        } catch (Exception e) {
            LogUtil.error(logger, e, "MQ发送消息异常! {0}", messageVo);
            //重试1次
            try {
                producer.send(message);
            } catch (Exception ex) {
                LogUtil.error(logger, ex, "MQ消息重发异常! {0}", message);
                //重发失败之后走http回调
                return true;
            }
        }
        return false;
    }

    /**
     * 租message对象
     * @param origin
     * @return
     */
    private Message toMessage(MessageVo origin) {
        Message message = new Message(
                origin.getTopic(),
                origin.getTopic(),
                JSON.toJSONString(origin.getBody()).getBytes(StandardCharsets.UTF_8)
        );
        if (StringUtils.isNotBlank(origin.getTag())) {
            message.setTag(origin.getTag());
        }
        message.setKey(origin.getKey());
        if (origin.getDelayTime() > 0L) {
            message.setStartDeliverTime(System.currentTimeMillis() + origin.getDelayTime() * 1000);
        }
        return message;
    }

    /**
     * http回调支付系统报关结果
     * @param url
     * @param json
     */
    public void declareNotifyToPayServer(String url, String json){
        HttpResponse response = HttpUtil.json(url, json, null, null);
        if (response == null || response.statusCode() != 200) {
            LogUtil.error(logger, "请求支付系统报关回调接口返回非200code,response:{0}", response);
            return;
        }
        if (StringUtils.isEmpty(response.bodyText())) {
            LogUtil.error(logger, "请求支付系统报关回调接口返回空报文,response:{0}", response);
            return;
        }
        String responseBody = response.bodyText();
        LogUtil.info(logger, "报关回调支付系统返回报文：{0}", responseBody);
    }

    /**
     * http回调支付系统支付结果
     * @param url
     * @param json
     */
    public void notifyToPayServer(String url, String json){
        //要发送的json报文
        HttpResponse response = HttpUtil.json(url, json, null, null);
        if (response == null || response.statusCode() != 200) {
            LogUtil.error(logger, "请求支付系统支付结果回调接口返回非200code,response:{0}", response);
            throw new SystemException(ExceptionEnum.E10014);
        }
        if (StringUtils.isEmpty(response.bodyText())) {
            LogUtil.error(logger, "请求支付系统支付结果回调接口返回空报文,response:{0}", response);
            throw new SystemException(ExceptionEnum.E10015);
        }
        String responseBody = response.bodyText();
        JSONObject notifyResp = JSON.parseObject(responseBody);
        //不成功抛出支付系统处理异常
        if (!ExceptionEnum.S00000.name().equals(notifyResp.getString("code"))) {
            throw new SystemException(ExceptionEnum.E10016, notifyResp.getString("msg"), TradeStatusEnum.FAIL);
        }
        //判断业务处理是否成功
        JSONObject bizResp = JSON.parseObject(notifyResp.getString("data"));
        if (!ExceptionEnum.S00000.name().equals(bizResp.getString("respCode"))) {
            throw new SystemException(ExceptionEnum.E10016, bizResp.getString("respMsg"), TradeStatusEnum.FAIL);
        }
    }

    /**
     * http回调支付系统退款结果
     * @param url
     * @param json
     */
    public void refundNotifyToPayServer(String url, String json){
        LogUtil.info(logger, "退款回调支付系统url: {0} 入参: {1}", url, json);
        //异步发起回调通知，通知结果暂不处理
        HttpResponse response = HttpUtil.json(url, json, null, null);
        logger.info("退款回调通知支付系统，返回：{}", JSON.toJSONString(response));
    }
}
